#pragma once

namespace UType
{
	template<typename T>
	class FString_Basic
	{
	public:
		FString_Basic()
		{
			Data = new T[1];
			*Data = '\0';
			Length = 0u;
		}
		FString_Basic(const T*Other)
		{
			if (Other == nullptr)
			{
				Data = new T[1];
				*Data = '\0';
			}
			else
			{
				size_t Len = FString_Basic_GetLength(Other);
				Data = new T[(1 + Len)];
				::std::memcpy(Data, Other, Len * sizeof(T));
				Data[Len] = '\0';
				Length = Len;
			}
		}
		~FString_Basic()
		{
			if (Data != nullptr)
			{
				delete[] Data;
				Data = nullptr;
			}
			Length = 0u;
		}
		FString_Basic(const FString_Basic&Other)
		{
			if (Other.Data == nullptr)
			{
				Data = new T[1];
				*Data = L'\0';
			}
			else
			{
				size_t Len = FString_Basic_GetLength(Other.Data);
				Data = new T[(1 + Len)];
				::std::memcpy(Data, Other.Data, Len * sizeof(T));
				Data[Len] = L'\0';
				Length = Len;
			}
		}
		FString_Basic&operator=(const FString_Basic&Other)
		{
			// TODO: insert return statement here
			if (&Other == this)
			{
				return *this;
			}
			if (Data != nullptr)
			{
				delete[] Data;
				Data = nullptr;
			}
			if (Other.Data == nullptr)
			{
				Data = new T[1];
				*Data = L'\0';
			}
			else
			{
				size_t Len = FString_Basic_GetLength(Other.Data);
				Data = new T[(1 + Len)];
				::std::memcpy(Data, Other.Data, Len * sizeof(T));
				Data[Len] = L'\0';
				Length = Len;
			}
			return *this;
		}
		FString_Basic&operator=(const T*Other)
		{
			// TODO: insert return statement here
			if (Data != nullptr)
			{
				delete[] Data;
				Data = nullptr;
			}
			if (Other == nullptr)
			{
				Data = new T[1];
				*Data = L'\0';
			}
			else
			{
				size_t Len = FString_Basic_GetLength(Other);
				Data = new T[(1 + Len)];
				::std::memcpy(Data, Other, Len * sizeof(T));
				Data[Len] = L'\0';
				Length = Len;
			}
			return *this;
		}
		FString_Basic& operator+=(const FString_Basic&Other)
		{
			// TODO: insert return statement here
			T*Temp = Data;
			size_t Len0 = FString_Basic_GetLength(Temp);
			size_t Len1 = FString_Basic_GetLength(Other.Data);
			Data = new T[(Len0 + Len1 + 1)];
			::std::memcpy(Data, Temp, Len0 * sizeof(T));
			::std::memcpy(Data + Len0, Other.Data, Len1 * sizeof(T));
			Data[Len0 + Len1] = L'\0';
			Length = Len0 + Len1;
			delete[] Temp;
			return *this;
		}
		FString_Basic& operator+=(const T*Other)
		{
			// TODO: insert return statement here
			return *this += FString_Basic<T>(Other);
		}
		FString_Basic operator + (const FString_Basic&Other)const
		{
			FString_Basic<T> NewString;
			if (NewString.Data != nullptr)
			{
				delete[] NewString.Data;
			}
			size_t Len0 = FString_Basic_GetLength(Data);
			size_t Len1 = FString_Basic_GetLength(Other.Data);
			NewString.Data = new T[(Len0 + Len1 + 1)];
			::std::memcpy(NewString.Data, Data, Len0 * sizeof(T));
			::std::memcpy(NewString.Data + Len0, Other.Data, Len1 * sizeof(T));
			NewString.Data[Len0 + Len1] = L'\0';
			return NewString;
		}
		FString_Basic operator + (const T&Other)const
		{
			return *this + FString_Basic<T>(Other);
		}
		bool operator==(const FString_Basic&Other)const
		{
			return FString_Basic_Equals(Data, Other.Data);
		}
		bool operator==(const T *Other)const
		{
			return FString_Basic_Equals(Data, Other);
		}
		bool operator!=(const FString_Basic&Other)const
		{
			return !FString_Basic_Equals(Data, Other.Data);
		}
		bool operator!=(const T *Other)const
		{
			return !FString_Basic_Equals(Data, Other);
		}
		T& operator[](size_t Index)
		{
			return Data[Index];
		}
		const T*operator *()const
		{
			return Data;
		}
		inline const T*GetData() const { return Data; }
		inline size_t GetLength()const { return Length; }
		bool EqualsTo(const T*Other)
		{
			return FString_Basic_Equals(Data, Other);
		}
		bool EqualsTo(const FString_Basic&Other)
		{
			return FString_Basic_Equals(Data, Other.Data);
		}
		void Append(const FString_Basic&Other)
		{
			*this += Other;
		}
		void Append(const T*Other)
		{
			*this += FString_Basic<T>(Other);
		}
	private:
		size_t FString_Basic_GetLength(const T*Other)const
		{
			size_t Len = 0u;
			if (Other != nullptr)
			{
				const T*Temp = Other;
				while (*Temp++ != L'\0') {
					Len++;
				};
			}
			return Len;
		}
		size_t FString_Basic_GetLength_WithType(const T*Other)const
		{
			return FString_Basic_GetLength(Other) * sizeof(T);
		}
	private:
		bool FString_Basic_Equals(const T*s1, const T*s2)const
		{
			if (s1 == nullptr || s2 == nullptr)
			{
				return false;
			}
			const T*Temp1 = s1;
			const T*Temp2 = s2;
			while (true) {
				T C1 = *Temp1++;
				T C2 = *Temp2++;
				if (C1 != C2 || (C1 == L'\0'&&C2 != L'\0') || (C1 != L'\0'&&C2 == L'\0'))
				{
					return false;
				}
				if (C1 == L'\0' || C2 == L'\0')
				{
					break;
				}
			};
			return true;
		}
	private:
		T * Data;
		size_t Length;
	};

	using FString = FString_Basic<wchar_t>;
	using FCString = FString_Basic<char>;
}
